/*
 * sockets.c
 *
 *  Created on: 12/10/2011
 *      Author: utn_so
 */

#include "sockets.h"

int32_t AFINET_abrir_conexion (unsigned short puerto)
{
  int32_t sockfd;
  struct sockaddr_in dirlocal;
  int32_t resp = 1;

  dirlocal.sin_family = AF_INET;
  dirlocal.sin_port = htons (puerto);
  dirlocal.sin_addr.s_addr = htonl (INADDR_ANY);

  if ( (sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
    {
      perror ("socket");
      return sockfd;
    }

  if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, &resp, sizeof(int32_t)) < 0)
    {
      perror ("setsockopt");
      return -1;
    }

  if (bind (sockfd, ( struct sockaddr*) &dirlocal, sizeof (dirlocal)) < 0)
    {
      perror ("bind");
      return -1;
    }

  if (listen (sockfd, 50) < 0) //Cantidad Maxima de Conexiones, Se puede setear por cfg
    {
      perror ("listen");
      return -1;
    }

  return sockfd;
}

int32_t AFUNIX_abrir_conexion (char *ruta)
{
  int32_t sockfd, tam;
  int32_t resp = 1;
  struct sockaddr_un dirlocal;

  dirlocal.sun_family = AF_UNIX;
  strcpy (dirlocal.sun_path, ruta);
  unlink (dirlocal.sun_path); /* Elimina el socket si ya existe. */
  tam = strlen (dirlocal.sun_path) + sizeof (dirlocal.sun_family);

  if ( (sockfd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
    {
      perror ("socket");
      return sockfd;
    }

  if (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, &resp, sizeof(int32_t)) < 0)
    {
      perror ("setsockopt");
      return -1;
    }

  if (bind (sockfd, ( struct sockaddr*) &dirlocal, tam) < 0)
    {
      perror ("bind");
      return -1;
    }

  if (listen (sockfd, 5) < 0)
    {
      perror ("listen");
      return -1;
    }

  return sockfd;
}

int32_t aceptar_conexion (int32_t sockfd)
{
  int32_t fd;
  int32_t tam;
  struct sockaddr cliente;

  tam = sizeof(struct sockaddr);

  if ( (fd = accept (sockfd, &cliente,(socklen_t *)&tam)) < 0)
    {
      perror ("accept");
      return fd;
    }

  return fd;
}

int32_t AFINET_conectar (char *dirIP, unsigned short puerto)
{
  int32_t sockfd;
  struct sockaddr_in dirserver;

  dirserver.sin_family = AF_INET;
  dirserver.sin_port = htons (puerto);
  dirserver.sin_addr.s_addr = inet_addr (dirIP);

  sockfd = socket (AF_INET, SOCK_STREAM, 0);

  if (connect (sockfd, ( struct sockaddr*) &dirserver, sizeof (dirserver)) < 0)
    {
      perror ("connect");
      return -1;
    }

  return sockfd;
}

int32_t AFUNIX_conectar (char *ruta)
{
  int32_t sockfd, tam;
  struct sockaddr_un dirserver;

  dirserver.sun_family = AF_UNIX;
  strcpy (dirserver.sun_path, ruta);

  tam = sizeof (dirserver.sun_family) + strlen (dirserver.sun_path);

  if ( (sockfd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
    {
      perror ("socket");
      return -1;
    }

  if (connect (sockfd, ( struct sockaddr*) &dirserver, tam) < 0)
    {
      perror ("connect");
      return -1;
    }

  return sockfd;
}

int32_t NIPC_enviar (int32_t sock, t_NIPC paquete)
{
  int32_t ret = 0; /* Para lo que retornan las funciones de sockets. */
  int32_t tam = 0;
  char *buffer = NULL;

  tam = sizeof (paquete.DescriptorID) + sizeof (paquete.PayloadDescriptor)
      + sizeof (paquete.PayloadLength) + sizeof (paquete.nroSector)
      + paquete.PayloadLength;
  buffer = serializar (paquete);


  ret = sendall (sock, buffer, &tam);

  liberarMemoria(&buffer);

  if (ret < 0)
    return -1;
  else
    return tam;
}

int32_t NIPC_recibir (int32_t sock, t_NIPC *paquete)
{
  int32_t tam_paquete = 0;
  int32_t tamHeader;
  int32_t payloadLength = 0;


  tamHeader = sizeof(paquete->DescriptorID) + sizeof(paquete->PayloadDescriptor) + sizeof (paquete->nroSector) + sizeof (paquete->PayloadLength);
  char *header = calloc(1,tamHeader);
  tam_paquete = recv(sock, header, tamHeader, MSG_WAITALL);
  if(tam_paquete <= 0)
	  return 0;
  else
  {
	memcpy(&payloadLength, header + sizeof(paquete->DescriptorID) + sizeof(paquete->PayloadDescriptor), sizeof(int32_t));

	char* buffer = malloc(tamHeader + payloadLength);
	memcpy(buffer, header, tamHeader);

	if (payloadLength > 0 )
	recv(sock, buffer + tamHeader, payloadLength, MSG_WAITALL);

	free(header);

	tam_paquete = tamHeader + payloadLength;
	if (tam_paquete > 0)
	deserializar (buffer, paquete);

	free (buffer);
	buffer = NULL;
	return tam_paquete;
  }
}

void obtenerDescriptor (char *ID)
{
  char buffer[64];
  int32_t numero;
  int32_t random = rand ();
  numero = ( int32_t) time (NULL);
  sprintf (buffer,"%d%d", numero, random);
  memset(ID, '\0', 20);
  strncat(ID, buffer,19);

}

void NIPC_crearpaquete (char *DescriptorID, char PayloadDescriptor, int32_t PayloadLength, char *Payload, uint32_t nroSector, t_NIPC *ret)
{

  /* Si el descriptor ID recibido es NULL genero uno nuevo, de lo contrario uso el que llega como argumento */
  if (DescriptorID == NULL)
  {
	  obtenerDescriptor (ret->DescriptorID);
  }
  else{
	  memcpy (ret->DescriptorID, DescriptorID, sizeof (ret->DescriptorID));
  }


  ret->PayloadDescriptor = PayloadDescriptor;
  ret->PayloadLength = PayloadLength;
  ret->nroSector = nroSector;
  if (PayloadLength > 0)
  {
      ret->payload = ( char *) calloc (ret->PayloadLength, sizeof(char));
      memcpy (ret->payload, Payload, ret->PayloadLength);

  }
  else
    ret->payload = NULL;

}

void NIPC_destruirpaquete (t_NIPC *paquete)
{
  if (paquete->PayloadLength > 0)
    {
      free (paquete->payload);
      paquete->payload = NULL;
    }

}

char *serializar (t_NIPC paquete)
{
  int32_t sizePaquete = sizeof (paquete.DescriptorID)
      + sizeof (paquete.PayloadDescriptor) + sizeof (paquete.PayloadLength)
      + sizeof (paquete.nroSector) + paquete.PayloadLength;
  char *buffer = NULL;
  buffer = ( char *) calloc (sizePaquete, sizeof(char));
  int32_t i, pos_payload = sizeof (paquete.DescriptorID)
      + sizeof (paquete.PayloadDescriptor) + sizeof (paquete.PayloadLength)
      + sizeof (paquete.nroSector);
  int32_t offset = 0;

  // Copio los primeros cuatro campos.
  memcpy (buffer, paquete.DescriptorID, sizeof (paquete.DescriptorID));
  offset = sizeof (paquete.DescriptorID);
  memcpy (buffer + offset,&(paquete.PayloadDescriptor),sizeof (paquete.PayloadDescriptor));
  offset += sizeof (paquete.PayloadDescriptor);
  memcpy (buffer + offset,&(paquete.PayloadLength),sizeof (paquete.PayloadLength));
  offset += sizeof (paquete.PayloadLength);
  memcpy (buffer + offset, &(paquete.nroSector),sizeof (paquete.nroSector));
  offset += sizeof (paquete.nroSector);
  // Copio el payload, sólo si PayloadLength es mayor a 0
  if (paquete.PayloadLength > 0)
    {
      for (i = 0; i < paquete.PayloadLength; i++)
        {
        buffer [i + pos_payload] = paquete.payload [i];
      }
  }

return buffer;
}

void deserializar (char *buffer, t_NIPC* paquete)
{
  int32_t offset=0;

  memcpy (paquete->DescriptorID, buffer, sizeof (paquete->DescriptorID));
  offset = sizeof (paquete->DescriptorID);

  memcpy (&paquete->PayloadDescriptor, buffer + offset, sizeof (paquete->PayloadDescriptor));
  offset += sizeof (paquete->PayloadDescriptor);

  memcpy (&paquete->PayloadLength, buffer + offset, sizeof (paquete->PayloadLength));
  offset += sizeof (paquete->PayloadLength);

  memcpy (&paquete->nroSector, buffer + offset, sizeof (paquete->nroSector));
  offset += sizeof (paquete->nroSector);

  if (paquete->PayloadLength > 0)
  {
    paquete->payload = ( char *) malloc (paquete->PayloadLength);
    memcpy (paquete->payload, buffer + offset, paquete->PayloadLength);
  }
  else
    paquete->payload = NULL;

}

int32_t sendall (int32_t socket, char *buf, int32_t *len)
{
int32_t total = 0; // cuántos bytes hemos enviado
int32_t bytesleft = *len; // cuántos se han quedado pendientes
int32_t n=0;

while (total < *len)
  {
    n = send (socket, buf + total, bytesleft, MSG_NOSIGNAL);
    if (n == -1)
      {
        return -1;
      }
    total += n;
    bytesleft -= n;
  }

*len = total; // devuelve aquí la cantidad enviada en realidad

return  0;
}
